/*
  The fat32.c file implements FAT-32 File System management functions
*/


#include <globals.h>
#include <klib.h>
#include <panic.h>

#include <hdd.h>

//Private Includes
#include <vfs_info.h>
#include <fat32.h>
#include <mbr.h>

/* Data structures */
struct vfs_driver *drv_fat32;

/* Private functions prototypes */

int vfs_fat32_analyze(struct vfs_partition_info *part_info);
void print_file_info(struct FAT32_directory_entry *dir_entry);
dword_t cluster_to_lba(struct vfs_partition_info *part_info,int cluster);

// mandatory VFS functions
int vfs_fat32_mount(char *device, char *mount_point, int flags);
int vfs_fat32_unmount(char *device);
int vfs_fat32_open(int *fd, char *path, int mode);
int vfs_fat32_close(int *fd);
int vfs_fat32_create(char *path, int flags);
int vfs_fat32_delete(char *path);
int vfs_fat32_read(int fd, void *buf, size_t count);
int vfs_fat32_write(int fd, void *buf, size_t count);
int vfs_fat32_walk(int fd, char *path);
int vfs_fat32_meta_read(int fd, char *key, void *value, size_t count);
int vfs_fat32_meta_write(int fd, char *key, void *value, size_t count);

/* Functions implementation */

int vfs_fat32_initialize()
  {    
    drv_fat32=(struct vfs_driver *)malloc(sizeof(struct vfs_driver));

    drv_fat32->vfs_mount = &vfs_fat32_mount;
    drv_fat32->vfs_unmount = &vfs_fat32_unmount;
    drv_fat32->vfs_open = &vfs_fat32_open;
    drv_fat32->vfs_close = &vfs_fat32_close;
    drv_fat32->vfs_create = &vfs_fat32_create;
    drv_fat32->vfs_delete = &vfs_fat32_delete;
    drv_fat32->vfs_read = &vfs_fat32_read;
    drv_fat32->vfs_write = &vfs_fat32_write;
    drv_fat32->vfs_walk = &vfs_fat32_walk;
    drv_fat32->vfs_meta_read = &vfs_fat32_meta_read;
    drv_fat32->vfs_meta_write = &vfs_fat32_meta_write;

    return 0;    
  }

int vfs_fat32_volume(struct vfs_partition_info *part_info)
  {
    struct VFS_FAT32_Custom *custom;
    int ret;

    custom=(struct VFS_FAT32_Custom *)malloc(sizeof(struct VFS_FAT32_Custom));

    // claim to be serving this driver
    part_info->driver = drv_fat32;
    // add custom data structure
    part_info->custom = custom;

    // fill in custom data structure
    ret=vfs_fat32_analyze(part_info);

    // if there are some problems
    // release allocated memory for custom info block
    if (ret!=0)
      {
	part_info->custom = NULL;
	free(custom);
      }

    return ret;
  }

// assume custom field has already been initialized
int vfs_fat32_analyze(struct vfs_partition_info *part_info)
  {
    struct VFS_FAT32_Custom *custom;      
    struct FAT32_Boot_Record *FAT32_BR;

    // this is a shortcut
    custom=(struct VFS_FAT32_Custom *)part_info->custom;

    // allocate a BPB structure
    FAT32_BR=(struct FAT32_Boot_Record *)malloc(sizeof(struct FAT32_Boot_Record));

    // Store a pointer to the Boot Record for future reference
    custom->FAT32_BR=FAT32_BR;

    //printf("Reading FAT32 BPB... ");
    hdd_read_sector(0,part_info->first_block,1,(char *)FAT32_BR);
    //printf("done.\n");

    custom->RootDirSectors = ((FAT32_BR->BPB_RootEntCount * 32) + (FAT32_BR->BPB_BytesPerSec - 1)) / FAT32_BR->BPB_BytesPerSec;

    if (FAT32_BR->BPB_FATSz16 != 0)
      custom->FATSz = FAT32_BR->BPB_FATSz16;
    else
      custom->FATSz = FAT32_BR->BPB_FATSz32;
    
    custom->FirstDataSector = FAT32_BR->BPB_ResSecCount + (FAT32_BR->BPB_NumFATs * custom->FATSz) + custom->RootDirSectors;

    custom->sectors_per_cluster = FAT32_BR->BPB_SecPerClus;


    printf("Partition start: %d\n",part_info->first_block);
    printf("Name: %s\n",FAT32_BR->BS_OEM_Name);
    printf("Bytes per sector: %d\n",FAT32_BR->BPB_BytesPerSec);
    printf("Sectors per cluster: %d\n",FAT32_BR->BPB_SecPerClus);
    printf("Sectors per track: %d\n",FAT32_BR->BPB_SecPerTrack);
    printf("Heads: %d\n",FAT32_BR->BPB_NumHeads);
    printf("Reserved sectors: %d\n",FAT32_BR->BPB_ResSecCount);
    printf("Start cluster of Root dir: %d\n",FAT32_BR->BPB_RootClus);

    printf("FAT size: %d sectors.\n",custom->FATSz);
    printf("Root dir: %d\n",custom->RootDirSectors);
    printf("Data sec: %d\n",custom->FirstDataSector);

    return 0;
  }


int vfs_fat32_diagnostics(void)
  {
    struct FAT32_Boot_Record *fat32_br;
    struct FAT32_directory_entry *fat32_dir;
    char *p;
    int i;

    p=(char *)malloc(1024*sizeof(char));
    fat32_br=(struct FAT32_Boot_Record *)p;

    // Clear buffer
    for (i=0;i<1024;i++)
      *(p+i)='\0';

    printf("Reading sector... ");
    hdd_read_sector(0,63,1,p);
    printf("done.\n");

    
    /*
    printf("Contents:\n");
    printf("==================================================\n");
    memory_dump(p,128);
    printf("==================================================\n");
    */

    printf("Name: %s\n",fat32_br->BS_OEM_Name);
    printf("Bytes per sector: %d\n",fat32_br->BPB_BytesPerSec);
    printf("Sectors per cluster: %d\n",fat32_br->BPB_SecPerClus);
    printf("Sectors per track: %d\n",fat32_br->BPB_SecPerTrack);
    printf("Heads: %d\n",fat32_br->BPB_NumHeads);
    //printf("Volume name: %s\n",fat32_br->volume_name);
    //printf("FAT name: %s\n",fat32_br->fat_name);

    printf("Reserved sectors: %d\n",fat32_br->BPB_ResSecCount);
    printf("Start cluster of Root dir: %d\n",fat32_br->BPB_RootClus);

    /*
    printf("Name @ %x\n",fat32_br->name);
    printf("FAT Name @ %x\n",fat32_br->fat_name);
    */

    //memory_dump(p+82,9);

    //i=fat32_br->sectors_per_cluster*fat32_br->root_dir_start_cluster;
    //i=fat32_br->reserved_sectors+fat32_br->root_dir_start_cluster*fat32_br->sectors_per_cluster;
    i=2076;

    printf("Reading sector... ");
    hdd_read_sector(0,63+i,1,p);
    printf("done.\n");

    /*
    printf("Contents:\n");
    printf("==================================================\n");
    memory_dump(p,128);
    printf("==================================================\n");
    */
    
    fat32_dir=(struct FAT32_directory_entry *)p;

    print_file_info(fat32_dir++);
    print_file_info(fat32_dir++);
    print_file_info(fat32_dir++);
    print_file_info(fat32_dir++);

    free(p);

    return 0;
  }


void print_file_info(struct FAT32_directory_entry *dir_entry)
  {
    if (dir_entry->attributes & FAT32_FA_VOLUME)
      return;

    printf("File name: %s\n",dir_entry->name);
    printf("     size: %d\n",dir_entry->size);
    printf("     clus: HI=%d LO=%d\n",dir_entry->cluster_high,dir_entry->cluster_low);    
  }

// returns the first data sector of a cluster
dword_t cluster_to_lba(struct vfs_partition_info *part_info,int cluster)
  {
    struct VFS_FAT32_Custom *custom;

    custom=(struct VFS_FAT32_Custom *)part_info->custom;

    long spc,fds;

    spc = custom->sectors_per_cluster;
    fds = custom->FirstDataSector;

    return ((cluster - 2) * spc ) + fds;
  }

/****************************************************
 * COMMON VFS MANDATORY FUNCTIONS
 ****************************************************/

int vfs_fat32_mount(char *device, char *mount_point, int flags)
  {
    return 0;
  }

int vfs_fat32_unmount(char *device)
  {
    return 0;
  }

int vfs_fat32_open(int *fd, char *path, int mode)
  {
    return 0;
  }

int vfs_fat32_close(int *fd)
  {
    return 0;
  }

int vfs_fat32_create(char *path, int flags)
  {
    return 0;
  }

int vfs_fat32_delete(char *path)
  {
    return 0;
  }

int vfs_fat32_read(int fd, void *buf, size_t count)
  {
    return 0;
  }

int vfs_fat32_write(int fd, void *buf, size_t count)
  {
    return 0;
  }

int vfs_fat32_walk(int fd, char *path)
  {
    return 0;
  }

int vfs_fat32_meta_read(int fd, char *key, void *value, size_t count)
  {
    return 0;
  }

int vfs_fat32_meta_write(int fd, char *key, void *value, size_t count)
  {
    return 0;
  }

